home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-06-16 | 13.2 KB | 337 lines | [TEXT/CCL2] |
- ;;; SEM 03/09/93 — wilma-mixin.lisp. Wilma-mixin is like a Fred-mixin, but with
- ;;; auto-fill. The fill routine is based on pixel width (not number of chars) so that
- ;;; it will work with proportional fonts.
- ;;;
- ;;; Paragraphs must be separated by a blank line. (Otherwise, they may be joined
- ;;; together when filling.) Auto-fill can happen only on Space and Newline chars.
- ;;; This takes care of the common cases without slowing down input too much. There
- ;;; will doubtless be times when things are not filled properly, so the user can force
- ;;; the entire buffer to refill by typing Control-Meta-TAB (see the end of this file
- ;;; for the key binding.)
- ;;;
- ;;; I never quite got around to cleaning up the code for public release so you might
- ;;; find some bugs. I hope you find it useful anyway.
- ;;;
- ;;; With gratitude to the net, I release this code to the public domain. However, I
- ;;; would appreciate it if you leave these comments. Please send bug reports to the
- ;;; author listed below.
- ;;;
- ;;; Wilma-mixin.lisp, version 0.91
- ;;; 03/09/93
- ;;;
- ;;; Stephen E. Miner
- ;;; miner@tc.pw.com
-
-
- ;;; SEM 08/15/91 — New idea that's not implemented: keep track of end-of-line
- ;;; before doing join since that's likely to be close to the next break.
-
- (in-package "CL-USER")
-
- #|
- ;;; This was my old definition, but now I use the internal function.
- (defun buffer-end-p (buffer &optional position)
- (>= (buffer-position buffer position) (buffer-size buffer)))
- |#
-
- (import 'ccl::buffer-end-p)
-
-
-
- (defclass wilma-mixin (fred-mixin)
- ()
- (:default-initargs :wrap-p t :view-font '("Chicago" 12)))
-
-
- (defclass wilma-window (wilma-mixin fred-window)
- ())
-
-
- ;;; SEM 10/08/91 — In b3 FRED-HPOS changed. Bill St. Clair sent us the
- ;;; fred-unwrapped-hpos.
- (defmethod fred-unwrapped-hpos ((w fred-mixin) &optional pos)
- "This recreates the behavior of the beta 2, FRED-HPOS."
- (ccl::frec-hpos (ccl::frec w) pos))
-
-
- (defmethod wilma-horz-pos ((w fred-mixin) &optional pos)
- (fred-unwrapped-hpos w pos))
-
-
- ;;; Implements auto-fill. We don't care about return value.
- ;;;
- ;;; SEM 03/06/92 — This function could definitely use some more work.
- (defmethod view-key-event-handler :around ((w wilma-mixin) char)
- (if (eql char #\newline)
- (let* ((buf (fred-buffer w))
- (was-eob (buffer-end-p buf))
- (was-eol (= (buffer-position buf) (buffer-line-end buf))))
- ;; add the #\newline as usual
- (call-next-method)
- ;; Check previous line...
- (when (wilma-line-needs-wrap-p w -1)
- (let ((prev-start (buffer-line-start buf nil -1)))
- ;; May need extra newline to separate paragraphs before refilling...
- (unless (or was-eol was-eob) (buffer-insert buf #\newline))
- (wilma-refill w prev-start)))
- (unless was-eol
- (wilma-refill w (buffer-line-start buf))))
- ;; All other chars...
- (call-next-method))
- ;; The char has now been handled the usual way, but we have to check for a space
- ;; that forces a wrap...
- (when (and (eql char #\space) (wilma-line-needs-wrap-p w))
- ;; Handle a new space that requires a wrap...
- (let ((buf (fred-buffer w)))
- (let ((was-eol (= (buffer-position buf) (buffer-line-end buf)))
- (was-space (unless (buffer-end-p buf) (eql (buffer-char buf) #\space))))
- (wilma-refill w (buffer-line-start buf))
- ;; There's a lot of tricky stuff here to get the new space in the right place!
- (cond ((eql (buffer-position buf) (buffer-line-start buf))
- nil)
- ((buffer-end-p buf)
- ;; The space got wasted by the wrap
- (buffer-insert buf #\space))
- ((and was-space (eql (buffer-position buf) (buffer-line-end buf)))
- (buffer-delete buf (buffer-position buf)))
- ((eql (buffer-position buf) (buffer-line-end buf))
- (buffer-insert buf #\space))
- ((and was-eol (eql (buffer-char buf) #\space))
- ; go forward over #\space
- (move-mark buf))
- (t nil))))
- (fred-update w)))
-
-
- (defmethod set-view-size :after ((w wilma-mixin) h &optional v)
- (declare (ignore h v))
- (wilma-refill-buffer w)
- (fred-update w))
-
-
- ;;; May not be adequate for multi-paragraph cutting and pasting, but reasonable for
- ;;; common cases. The user can always force the entire buffer to refill by using
- ;;; Control-Meta-Tab (see end of this file for key-binding.)
-
- (defmethod paste :after ((w wilma-mixin))
- (ed-fill-top-level w))
-
- (defmethod cut :after ((w wilma-mixin))
- (ed-fill-top-level w))
-
- (defmethod clear :after ((w wilma-mixin))
- (ed-fill-top-level w))
-
-
- ;;; Fills current paragraph...
- (defmethod ed-fill-top-level ((w wilma-mixin))
- (let* ((buf (fred-buffer w))
- ;; find start of section around current position...
- (start (wilma-buf-section-start buf)))
- (wilma-refill w start)))
-
-
- (defmethod wilma-refill :before ((w wilma-mixin) start)
- (declare (ignore start))
- (set-fred-hscroll w 0)
- (fred-update w))
-
- (defmethod wilma-refill ((w wilma-mixin) start)
- (when start
- (wilma-fill-section w start (wilma-pixel-width w) " ")))
-
- (defmethod wilma-refill :after ((w wilma-mixin) start)
- (declare (ignore start))
- (fred-update w))
-
-
- ;;; SEM 08/14/91 — New and improved
- (defmethod wilma-refill-buffer ((w wilma-mixin))
- (flet ((find-next-start (buf pos)
- (buffer-not-char-pos buf #.(coerce '(#\space #\return) 'string)
- :start pos)))
- (let ((buf (fred-buffer w)))
- ;; all the work is done by side-effects in WILMA-REFILL
- (do ((next-start (find-next-start buf 0)
- (find-next-start buf (wilma-refill w next-start))))
- ((or (not next-start) (buffer-end-p buf next-start)) nil)
- ;; empty body
- ))))
-
-
- (defun wilma-buf-paragraph-start-p (buffer line-start)
- "Returns T if CH looks like it starts a paragraph, NIL otherwise."
- (not (member (wilma-buf-line-non-wsp-char buffer line-start)
- '(nil #\space #\return #\linefeed #\tab #\Null))))
-
- (defun wilma-buf-section-start (buf)
- "Complicated way to find start of section around current position in BUF."
- (let* ((bpos (buffer-line-start buf))
- (bsize (buffer-size buf)))
- ;; We have to be careful about the end of buffer, maybe should use BUFFER-END-P
- ;; here.
- (when (and (> bsize 0) (not (= bpos bsize))
- (wilma-buf-paragraph-start-p buf bpos))
- (do ((pos bpos)
- (pastp nil))
- (pastp pos)
- (multiple-value-bind (next-start overflowp)
- (buffer-line-start buf pos -1)
- (setq pastp
- (or overflowp
- (not (wilma-buf-paragraph-start-p buf next-start))))
- (unless pastp (setq pos next-start)))))))
-
-
- ;;; NOTE: The 20 was determined empirically (read "KLUDGE"). The idea is
- ;;; WILMA-PIXEL-WIDTH gives a reasonable answer for the number of pixels used for text.
- ;;; This number could probably use some tuning.
- (defmethod wilma-pixel-width ((w fred-window))
- ;; subtracting for the scrollbar and border
- (- (point-h (view-size w)) 20))
-
- (defmethod wilma-pixel-width ((item dialog-item))
- (- (point-h (view-size item)) (dialog-item-width-correction item)))
-
- (defmethod wilma-pixel-width ((w simple-view))
- (point-h (view-size w)))
-
-
- (defun wilma-buf-line-non-wsp-char (buffer line-start)
- "First non-whitespace character in line starting at LINE-START in BUFFER or NIL
- if there isn't one."
- (let ((first-non-wsp-pos
- (buffer-not-char-pos buffer #\space :start line-start
- :end (buffer-line-end buffer line-start))))
- (when first-non-wsp-pos
- (buffer-char buffer first-non-wsp-pos))))
-
- ;;; Normally checks current line, but LINE-COUNT adds offset (use -1 for previous line.)
- (defmethod wilma-line-needs-wrap-p ((w wilma-mixin) &optional (line-count 0))
- (>= (wilma-horz-pos w (buffer-line-end (fred-buffer w) nil line-count))
- (wilma-pixel-width w)))
-
- (defun wilma-buf-strip-line (buffer start strip-char)
- "Strips STRIP-CHAR (string or char or NIL for none) from beginning of line and
- trailing spaces from line starting at START in BUFFER. Caller must guarantee that
- START is start of a line."
- (when strip-char
- (let* ((eoln (buffer-line-end buffer start))
- (strip-pos (or (buffer-not-char-pos buffer strip-char :start start :end eoln)
- eoln)))
- (buffer-delete buffer start strip-pos)))
- (let* ((end (buffer-line-end buffer start))
- (end-non-space (buffer-not-char-pos buffer #\space :start
- start :end end :from-end t)))
- (when (and end-non-space (< end-non-space end))
- (buffer-delete buffer (1+ end-non-space) end))))
-
-
- ;;; SEM 08/15/91 — If you want double spaces after periods or other end of sentence
- ;;; characters you should fix up this function.
- (defun wilma-buf-join-prev-line (buffer start)
- "Joins line starting at START to previous line in BUFFER."
- (unless (zerop start)
- (buffer-char-replace buffer #\space (1- start))))
-
-
- ;;; Fill-predicate is a function that returns T if the given char starts a line that
- ;;; should be filled...
- (defmethod wilma-fill-section ((w wilma-mixin) start pixel-width strip-char)
- (let ((buf (fred-buffer w)))
- (wilma-buf-strip-line buf start strip-char)
- (loop
- (setq start (wilma-wrap-line w start pixel-width))
- (multiple-value-bind (next-start overflowp) (buffer-line-start buf start 1)
- (when (or overflowp
- (= next-start (buffer-size buf))
- (not (wilma-buf-paragraph-start-p buf next-start)))
- (return next-start))
- (wilma-buf-strip-line buf next-start strip-char)
- (wilma-buf-join-prev-line buf next-start) ;joins next to start line
- ))))
-
-
-
- ;;; Date: 14 Aug 91 15:36:19 U
- ;;; From: "Seth Powsner" <seth_powsner@yccatsmtp.ycc.yale.edu>
- ;;; [gives us a new version (not shown here).]
- ;;;
- ;;; SEM -- This should be faster for long lines... I made a few additional changes.
- ;;; The big problem with the original code was that it was taken from a character-count
- ;;; based routine that already knew its goal-column, but in this case we have to
- ;;; search for it. By the way, on long lines it would be faster to search from the
- ;;; front so we do that too.
-
- (defmethod wilma-wrap-line ((w wilma-mixin) line-start pixel-width)
- "Recursively wraps single line (as many times as necessary) and returns
- position of start of last resulting line. Caller must guarantee that
- LINE-START is the position of a line."
- (let* ((buffer (fred-buffer w))
- (line-end (buffer-line-end buffer line-start))
- (line-width (wilma-horz-pos w line-end)))
- (if (<= line-width pixel-width)
- line-start
- (let ((break-pos (if (> line-width (* 2 pixel-width))
- ;; Search forward from start of long line...
- (do ((leading-space nil)
- (p (buffer-char-pos buffer #\space :start line-start
- :end line-end)
- (buffer-char-pos buffer #\space :start (1+ p)
- :end line-end)))
- ((or (null p) (> (wilma-horz-pos w p) pixel-width))
- (or leading-space p))
- (setf leading-space p))
- ;; Search backward from end of not-so-long line
- (do ((trailing-space nil)
- (p (buffer-char-pos buffer #\space :start line-start
- :end line-end :from-end t)
- (buffer-char-pos buffer #\space :start line-start
- :end p :from-end t)))
- ((or (null p) (<= (wilma-horz-pos w p) pixel-width))
- (or p trailing-space))
- (setf trailing-space p)))))
- (if break-pos
- (progn
- (buffer-char-replace buffer #\newline break-pos)
- (wilma-wrap-line w (1+ break-pos) pixel-width))
- ;else, give up on a line of a single long word
- line-start)))))
-
-
-
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-
- ;;; Most fred-windows won't do anything...
- (defmethod wilma-refill-buffer ((w fred-mixin))
- (declare (ignore w))
- nil)
-
- ;;; Control-Meta-Tab will force refill of entire buffer. It things don't look right,
- ;;; give this a try...
- (unless (comtab-get-key *comtab* '(:control :meta #\tab))
- (comtab-set-key *comtab* '(:control :meta #\tab) 'wilma-refill-buffer))
-
-
- #|
-
- (setq www (make-instance 'wilma-window))
-
- (defclass wilma-item (wilma-mixin editable-text-dialog-item)
- ())
-
- (setq w (make-instance 'window
- :view-subviews (list (make-instance 'wilma-item
- :view-size #@(150 150)
- :dialog-item-text "This is a test")
- (make-instance 'wilma-item
- :view-size #@(150 150)
- :scroll-p t
- :dialog-item-text "This is not a test"))))
-
- |#
-